﻿Imports System.Text
Imports System.Xml
Imports System.Net
Imports System.Web

Namespace Scraping
    Partial Class TwitterClient
        Implements ITwitterClient
        ''' <summary>
        ''' Twitterへログインする処理中のデータを保持します。
        ''' </summary>
        ''' <remarks></remarks>
        Private Class LoginBundle
            Private _result As LoginResult
            Public Property Result() As LoginResult
                Get
                    Return _result
                End Get
                Set(ByVal value As LoginResult)
                    _result = value
                End Set
            End Property

            Private _authenticityToken As String
            Public Property AuthenticityToken() As String
                Get
                    Return _authenticityToken
                End Get
                Set(ByVal value As String)
                    _authenticityToken = value
                End Set
            End Property

            Private _authenticityTokenFiledName As String
            Public Property AuthenticationTokenFiledName() As String
                Get
                    Return _authenticityTokenFiledName
                End Get
                Set(ByVal value As String)
                    _authenticityTokenFiledName = value
                End Set
            End Property

            Private _userNameFieldName As String
            Public Property UserNameFieldName() As String
                Get
                    Return _userNameFieldName
                End Get
                Set(ByVal value As String)
                    _userNameFieldName = value
                End Set
            End Property

            Private _passwordFieldName As String
            Public Property PasswordFieldName() As String
                Get
                    Return _passwordFieldName
                End Get
                Set(ByVal value As String)
                    _passwordFieldName = value
                End Set
            End Property
        End Class

        ''' <summary>
        ''' Twitterにログインします。
        ''' </summary>
        ''' <param name="userName">ユーザー名</param>
        ''' <param name="password">パスワード</param>
        ''' <returns>ログイン結果</returns>
        ''' <remarks></remarks>
        Public Function Login(ByVal userName As String, ByVal password As String) As LoginResult Implements ITwitterClient.Login
            Dim bundle As LoginBundle = LoginGet()
            Select Case bundle.Result
                Case LoginResult.Success
                    LoginPost(userName, password, bundle)
            End Select
            Return bundle.Result
        End Function

        ''' <summary>
        ''' Twitterのログイン画面を取得して解析します。
        ''' </summary>
        ''' <returns>処理中のデータを保持するオブジェクト</returns>
        ''' <remarks></remarks>
        Private Function LoginGet() As LoginBundle
            Dim bundle As New LoginBundle
            Dim url As String = My.Resources.TwitterUrl
            Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
            req.SetTwitterGetHeaders(Nothing, _cookies)
            Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
                Select Case res.StatusCode
                    Case HttpStatusCode.OK
                        res.LoadCookies(_cookies)
                        Dim xmlRes As XmlDocument = res.GetResponseAsXHtml()
                        Dim xnm As XmlNamespaceManager = xmlRes.GetXHtmlNameSpaceManager("x")
                        bundle.AuthenticityToken = xmlRes.SelectSingleNode("//x:input[@id='authenticity_token']", xnm).Attributes("value").Value
                        bundle.AuthenticationTokenFiledName = xmlRes.SelectSingleNode("//x:input[@id='authenticity_token']", xnm).Attributes("name").Value
                        bundle.UserNameFieldName = xmlRes.SelectSingleNode("//x:input[@id='username']", xnm).Attributes("name").Value
                        bundle.PasswordFieldName = xmlRes.SelectSingleNode("//x:input[@id='password']", xnm).Attributes("name").Value
                        bundle.Result = LoginResult.Success
                    Case Else
                        bundle.Result = LoginResult.ErrorOccured
                End Select
            End Using
            Return bundle
        End Function

        ''' <summary>
        ''' Twitterにログイン情報をポストして結果を取得します。
        ''' </summary>
        ''' <param name="userName">ユーザー名</param>
        ''' <param name="password">パスワード</param>
        ''' <param name="bundle">処理中のデータを保持するLoginBundle</param>
        ''' <remarks></remarks>
        Private Sub LoginPost(ByVal userName As String, ByVal password As String, ByVal bundle As LoginBundle)
            Dim url As String = String.Format("{0}{1}", My.Resources.TwitterSecureUrl, My.Resources.TwitterSessionPath)
            Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
            req.SetTwitterPostHeaders("http://twitter.com/", _cookies)
            Dim sbPost As New StringBuilder()
            sbPost.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(bundle.AuthenticationTokenFiledName), HttpUtility.UrlEncode(bundle.AuthenticityToken))
            sbPost.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(bundle.UserNameFieldName), HttpUtility.UrlEncode(userName))
            sbPost.AppendFormat("{0}={1}", HttpUtility.UrlEncode(bundle.PasswordFieldName), HttpUtility.UrlEncode(password))
            Dim strPost As String = sbPost.ToString()
            req.PostString(strPost)
            Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
                Select Case res.StatusCode
                    Case HttpStatusCode.Found
                        res.LoadCookies(_cookies)
                        _authenticityTokenFiledName = bundle.AuthenticationTokenFiledName
                        _authenticityToken = bundle.AuthenticityToken
                        _isLogined = True
                        _userName = userName
                        bundle.Result = LoginResult.Success
                    Case HttpStatusCode.OK
                        bundle.Result = LoginResult.Failed
                    Case Else
                        bundle.Result = LoginResult.ErrorOccured
                End Select
            End Using
        End Sub

        ''' <summary>
        ''' Twitterからログアウトします。
        ''' </summary>
        ''' <returns>ログアウト結果</returns>
        ''' <remarks></remarks>
        Public Function Logout() As LogoutResult Implements ITwitterClient.Logout
            If Not IsLogined Then
                Throw New InvalidOperationException(StrThisInstanceIsNotLoginedYet)
            End If

            Dim funcResult As LogoutResult
            Dim url As String = String.Format("{0}{1}", My.Resources.TwitterUrl, My.Resources.TwitterSessionDestroyPath)
            Dim req As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
            req.SetTwitterPostHeaders("http://twitter.com/", _cookies)
            Dim sbReq As New StringBuilder()
            sbReq.AppendFormat("{0}={1}", HttpUtility.UrlEncode(_authenticityTokenFiledName), HttpUtility.UrlEncode(_authenticityToken))
            Dim strReq As String = sbReq.ToString()
            req.PostString(strReq)
            Using res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
                Select Case res.StatusCode
                    Case HttpStatusCode.Found
                        _cookies = New CookieContainer()
                        _authenticityToken = Nothing
                        _authenticityTokenFiledName = Nothing
                        _isLogined = False
                        funcResult = LogoutResult.Success
                    Case HttpStatusCode.OK
                        funcResult = LogoutResult.Failed
                    Case Else
                        funcResult = LogoutResult.ErrorOccured
                End Select
            End Using
            Return funcResult
        End Function
    End Class
End Namespace